package com.yyn.server.dao;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

/**
 * @author: lxh
 * @date: 2022/12/16 21:28
 * @version: 1.0
 * 服务端
 */
public class ChatServer {

    public static void main(String[] args) {

        try (
                // 创建ServerSocketChannel
                ServerSocketChannel ssc = ServerSocketChannel.open();
        ) {
            // 绑定端口
            ssc.socket().bind(new InetSocketAddress(8080));
            // 设置非阻塞
            ssc.configureBlocking(false);
            // 创建Selector多路选择器
            Selector selector = Selector.open();
            // 将服务端的Channel注册到Selector上，并告知对客户端的连接事件感兴趣
            ssc.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务端启动成功...");
            // 循环等待事件发生
            while (true) {
                // 阻塞等待，有就绪事件是接收阻塞状态
                int select = selector.select();
                if (0 == select) continue;
                // 获取所有就绪状态的Channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 遍历
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    // 获取当前SelectionKey
                    SelectionKey selectionKey = iterator.next();
                    // 处理就绪事件
                    handle(selectionKey);
                    // 移除当前就绪Channel，防止重复处理
                    iterator.remove();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 处理的就绪就绪事件
     * @param selectionKey
     */
    private static void handle(SelectionKey selectionKey) throws IOException {
        if (selectionKey.isAcceptable()) {
            handleAccept(selectionKey);
        }
        if (selectionKey.isReadable()) {
            handleRead(selectionKey);
        }
    }

    /**
     * 处理读就绪事件
     * @param selectionKey
     * @throws IOException
     */
    private static void handleRead(SelectionKey selectionKey) throws IOException {

        // 获取Selector
        Selector selector = selectionKey.selector();
        // 获取当前客户端和服务端通信的Channel
        SocketChannel sc = (SocketChannel) selectionKey.channel();
        // 设置非阻塞
        sc.configureBlocking(false);
        // 创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 读取数据
        StringBuilder msg = new StringBuilder();
        int len = 0;
        while ( (len = sc.read(buffer)) > 0 ) {
            msg.append( new String(buffer.array(), 0, len) );
            // 清空缓冲区，即position置为0，limit置为capacity
            buffer.clear();
        }

        if (msg.length() > 0) {
            System.out.println(sc.getRemoteAddress() + ": " + msg);
            // 将消息广播到自己除外的所有客户端
            broadcastMessage(selectionKey, msg.toString());
        }
        sc.register(selector, SelectionKey.OP_READ);
    }

    /**
     * 广播消息到自己除外的所有客户端
     * @param key
     * @param msg 消息
     * @throws IOException
     */
    private static void broadcastMessage(SelectionKey key, String msg) throws IOException {
        // 获取Selector
        Selector selector = key.selector();
        // 获取所有注册到Selector上的客户端Channel
        Set<SelectionKey> keys = selector.keys();
        // 遍历
        Iterator<SelectionKey> iterator = keys.iterator();
        while (iterator.hasNext()) {
            SelectionKey selectionKey = iterator.next();
            // 获取当前Channel
            SelectableChannel channel = selectionKey.channel();
            // 如果是SocketChannel并且当前SocketChannel不是发送消息过来的那个人
            if (channel instanceof SocketChannel && channel != key.channel()) {
                // 广播消息
                SocketChannel sc = (SocketChannel) channel;
                sc.write(ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8)));
            }
        }
    }

    /**
     * 处理连接就绪事件
     * @param selectionKey
     * @throws IOException
     */
    private static void handleAccept(SelectionKey selectionKey) throws IOException {

        // 获取Selector
        Selector selector = selectionKey.selector();
        // 获取服务端Channel
        ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
        // 接受客户端的连接请求
        SocketChannel sc = ssc.accept();
        // 设置非阻塞
        sc.configureBlocking(false);
        // 将客户端Channel注册到Selector上，并告知对读事件感兴趣
        sc.register(selector, SelectionKey.OP_READ);
        // 写一个数据到客户端
        sc.write(ByteBuffer.wrap("欢迎使用大海聊天室...".getBytes(StandardCharsets.UTF_8)));

    }

}

